视图系统
数据库系统的一项重要目标在于便利数据的存 取操作,而视图系统的设立则帮助我们屏蔽了数据管理的复杂性,使得我们的应用可以更好地将精力集中于同它们相关的数据上面来。
(数据管理的三层模型)
而作为 PostgreSQL 学习者,我们应当把我们的精力放到 PostgreSQL 自身的视图系统上面来,而各种语言所为之设立的 ORM 框架,则可以作为一项补充学习的内容,选择其它的一个时间来加以理解(理解数据应用开发人员的想法,可以促进我们更好地改进数据库的设计与工作方法)。
PostgreSQL 视图简介
在 PostgreSQL 之中,视图按照类别(一般类型视图、物化视图)经由不同的模块加以实现,尽管内部细节不一,但是它们的导向都是一致:帮助数据应用集成分散于数据库系统各处的数据。
我们将先向读者介绍一般类型视图,它是我们理解视图系统最好的切入点。
让我们引入一个简单的场景,假定我们正在设计一套智能家居工作方案,购买了一组传感器,将其放置于房屋的各个区域,每一个传感器都有一张为其设立的数据表:
/* 为客厅设立的温度传感器数据表 */
CREATE TABLE temperature_sensor_living_room (
temperature DOUBLE PRECISION, /* 记录客厅的温度 */
record_time TIMESTAMP /* 记录数据的时间 */
);
时序数据简介
一般而言,我们把这些按照时间顺序不断存储下来的数据统称为时序数据,它们在科研(如国际Argo组织为了获取全球海洋的温盐深信息,在各处设立了数千个传感器,按照十天一次的规律收集海洋数据),工业(如为了监测生产设备而设立的摄像头所实时获取的图像信息)中应用非常广泛。
伴随着物联网科技的兴起,工厂的数字化转型,ESG 投资的兴起,时序数据的场景将会越发丰富,未来,专门应用于处理时序数据的时序数据库将会成为数据库科学领域中一门重要的分支。
在 PostgreSQL 生态里面,Timescale 公司目前是 PostgreSQL 在时序数据领域的代表性企业,它们基于 PostgreSQL 设计的 TimescaleDB 在时序数据领域应用非常广泛。
/* 为家门口设立的摄像头传感器数据表 */
CREATE TABLE camera_sensor_gate (
image BYTEA, /* 记录进入者的图像 */
allow_to_come BOOLEAN, /* 是否允许进入? */
record_time TIMESTAMP /* 记录数据的时间 */
);
请使用 BYTEA 而不是 TEXT 来存储纯二进制数据
对于需要以原始形态保留的数据,使用 BYTEA 而不是以 TEXT 为代表的字符串类型进行存储,是一种更为合适的做法。
在 PostgreSQL 中,字符串数据的目的,在于存储易于用户易于阅读的文本,这就使得我们在使用字符串数据类型管理我们数据的时候,PostgreSQL 将要同用户计算机上面安装的字符集合作以展开工作(这种转换,可能会对存储的数据造成一定的影响,尽管对于用户而言是透明的);同时,PostgreSQL 对于过长文本,将会应用压缩措施以节省存储空间(这又带来了数据损坏的风险,尽管对于用户而言一样是透明的)。
因此,如果我们希望存入的数据在二进制上面完全没有改动(而不仅仅是呈现结果上没有改动)的话,请使用 BYTEA 作为存储用的数据类型,它对于存入数据的限制相较于字符串类型而言,更少一些(比如,对于不存在于字符集中的二进制,字符串数据类型将会拒绝存储,而 BYTEA 不会),同时,它将完全不会改动存入的二进制数据。
缺点在于,BYTEA 使得很多针对于某种数据的特定优化举措无法展开(如我们使用 BYTEA 存入 Json 文档的话,除非我们做类型转换,否则 PostgreSQL 为 Json 专门设计的许多组件,无法得到应用)。
/* 红外线传感器数据表 */
CREATE TABLE infrared_sensor (
clean_path PATH, /* 检测到人进入以后,机器人打扫房间的路径 */
record_time TIMESTAMP /* 进入卧室的时间 */
);
复合数据类型是数据类型的一种组合
除基本数据类型(如数字和字符串)与为某种特定目的设立的数据类型(如 Json,时间,货币)之外,PostgreSQL 还支持复合数据类型,它们实际上便是基本数据类型、特定数据类型乃至于其它复合数据类型的组合体,可以视作“表中之表”,在一个数据列的区域,存储信息密度远高于普通数据类型的数据。
如 PATH 数据类型,实际上便是用数个点(Point)来描述的线段,而点本身又是由两个数字(x, y)描述的一种数据。
现在,我们希望面向不同需求的应用开发者开放不同的数据(从安全的角度来说,强制要求应用开发者们书写诸如 SELECT x1, x2, ... 这样的 SQL 语句显然是不可能的,直接限制应用开发者们能够获取到的数据范围(即 FROM 所对应的部分)明显是更为可行的做法),依托视图系统我们将可以轻松达到这一目的。
从不信任,永远验证
—— 零信任网络核心理念
在 PostgreSQL 中,使用 CREATE VIEW 就可以为我们创建出一张视图来,其基本的语法如下:
CREATE VIEW name AS /* 查询语句 */;
下面我们通过数个场景,阐述视图的应用:
场景一:获取试图进入家内失败的“闯入者”图像
假定面向一些安全类的应用,我们希望向他们开放那些视图进入家中但是没能成功的“入侵者”数据集,那么我们可以设立如下的 intruder 视图(可以发现,视图从某种角度来看,是对于查询语句的封装):
CREATE VIEW intruder AS SELECT image, record_time FROM camera_sensor_gate WHERE allow_to_come = FALSE;
可以经由 PostgreSQL 视图修订源数据表数据
与其它诸多的数据库系统实现不一样,PostgreSQL 的视图经由规则系统实现(The Rule System)。
CREATE VIEW 将会在 pg_rewrite 数据表处登记一条转换规则,并根据视图的名称注册一条“关系”(PostgreSQL 的“关系”不仅指代数据表,还涵盖了物化视图、视图等许多其它的对象)。
这样,我们在执行对某张视图的某种查询的时候,PostgreSQL 的规则系统将会指导 SQL 处理相关的模块把我们对于视图的操作,转换为对应各张数据表的操作,也由此使得我们可以经由视图,修订源数据表的数据。
而应用开发者希望查询“闯入者”有关信息的时候,就可以编写如下的代码:
/* 使用视图,尽管内部原理不同,但是外部使用上,与普通数据表,基本没有差异 */
SELECT image, record_time FROM intruder;
场景二:将进入家中的时间与卧室机器人监测到人的时间结合起来
在数据分析领域中,将表面上毫不相关的数据项组合起来分析,找出潜在的关联,是一门非常有趣的学问(数据挖掘),现在假定我们对住客归家与进入卧室的时间关联产生了兴趣,需要设计一张视图,能够同时将进入家中的时间同卧室传感器监测到有人归家的时间查询返回,则我们可以尝试编写如下的代码:
CREATE VIEW record_time
AS SELECT camera_sensor_gate.record_time AS record_time, '归家时间' AS source FROM camera_sensor_gate
UNION SELECT infrared_sensor.record_time, '进入卧室时间' FROM infrared_sensor;
这里,就又牵涉到了 AS 子句
与一些多表查询的知识,让我们继续逐步分解他们。
AS 子句可以对结果列的名称做重命名
一般而言,倘若我们不指定结果列的名称(关系运算的结果也是关系型数据,即数据表形式的数据),那么对应数 据列的名称,将会填充结果列的属性名称。
但是使用 AS 子句,我们可以对其做出改变,如在这里,我们重命名了 record_time 与 source 两个属性列,分别代表数据记录的时间,以及数据的来源。
同时,我们可以注意到,此处视图所对应的查询语句,属于平行并列的 SELECT(而不是子查询),在这里,就需要我们对多表查询的知识,做一定的了解,参考如下:
多表查询
SQL 语言设计之初就是为了存储大规模的结构化数据,因此自然而然地要对来自于多个数据表的数据提供一定的支持,这种支持,体现到实践中来,就是“多表查询”,一般而言,我们可以按照如下的两种方法来做查询:
- 连接运算(Join, Full Join 等)
这种运算,将会根据对应多张数据表中的某个数据列的匹配情况,结合对应连接运算的特点(如 Right Join 将会返回右表的全部数据,以及返回左表匹配的对应数据;而 Anti Join 将返回两张数据表中不匹配的部分等),对两张数据表的数据进行整合(最终的结果将是多张数据表被整合为一张表)。 - 交并运算(Union,EXCEPT 等)
这种运算的特点在于按照集合的观点来理解多张数据表中的数据,只不过此处的数据表抽象层次更高,属于是按照查询语句所产生的数据表对应匹配查询语句所对应的数据表了(因为关系型查询的执行结果也是关系型数据)。
两种运算殊途同归,都是对于多张数据表数据的整合,因此我们只需要按照集合的图例去加以理解,自然而然就可以理解明白。
就此,我们对于 PostgreSQL 的一般类型视图建立了一定的了解,他们可以帮助我们整合来自于各处的数据,面向某种需求,将某种数据开放给对应的数据应用。
物化视图简介
相较于使用 CREATE VIEW
创建的一般类型视图,CREATE MATERIALIZED VIEW
创建的物化视图将会真实的把数据存储起来,两者的区别,简单来说,一般视图会指导查询模块将对视图的操作为对于多张数据表的操作,物化视图的原理则是在内部系统中开辟一块新的区域以便于存储数据,因此我们可以使用 REFRESH MATERIALIZED VIEW
对物化视图的数据进行刷新(更新被缓存的数据),而不能够用此更新一般视图的数据(因为对于一般视图的操作将会被改写为对于多张数据表的操作)。
除此之外,在内部实现上,CREATE VIEW
与 CREATE MATERIALIZED VIEW
实际上被交付给了不同的内部模块加以实现,两者只是在对外情况上存在类似之处。